using System;
using System.Collections.Generic;

namespace WiMo.Games.Drawables
{
    public abstract class SpriteBase : ISprite
    {        
        public virtual float Mass { get; set; }        

        private bool _isStatic = true;
        public virtual bool IsStatic { get {return _isStatic;} set {_isStatic = value;} }
        public virtual Vector2 Force { get; set; }

        /// <summary>
        /// Set's the color used as transparency for the control
        /// </summary>
        public virtual Color ColorKey { get; set; }

        public virtual bool AffectedByGravity { get; set; }                
        
        public virtual GameTime LastUpdated { get; set; }
        public virtual bool IsTransient { get; set; }
        public virtual bool IsDirty { get; set; }
        public virtual bool Moved { get; set; }
        private Origin _origin;
        public virtual Origin Origin
        {
            get
            {
                return _origin;
            }
            set
            {
                if(_origin != value)
                {
                    Invalidate();
                    _origin = value;
                }
            }
        }

        private RenderStrategy _renderStrategy;
        public virtual RenderStrategy RenderStrategy
        {
            get
            {
                return _renderStrategy;
            }
            set
            {
                if(_renderStrategy != value)
                {
                    Invalidate();
                    _renderStrategy = value;
                }
            }
        }
        public virtual int ZIndex { get; set; }
        public virtual Vector2 Scale { get; set; }
        public virtual PointF LastPosition { get; set; }
        public virtual CollisionShapes CollisionShape { get; set; }
        public virtual float Torque { get; set; }
        public virtual float Rotation { get; set; }
        public virtual float AngularVelocity { get; set; }
        public virtual float Rotations { get; set; }
        public virtual List<Location> CollisionDetectionPoints { get; set; }
        public virtual List<CollisionSphere> CollisionSpheres { get; set; }
        public virtual IPhysicsManager PhysicsManager { get; set; }
        public virtual ICollisionManager CollisionManager { get; set; }

        private bool _isVisible;
        public virtual bool IsVisible
        {
            get
            {
                return _isVisible;
            }
            set
            {
                if (_isVisible != value)
                {
                    Invalidate();
                    _isVisible = value;
                }
            }
        }

        private List<Quadrant> _quandrants;
        public virtual List<Quadrant> Quandrants
        {
            get{return _quandrants;}
        }

        public void Invalidate()
        {            
            if (DrawInformation is IDisposable)
                ((IDisposable)DrawInformation).Dispose();

            DrawInformation = null;
            IsDirty = true;
            Moved = true;
            _boundingRect = Rectangle.Empty;
        }

        public SpriteBase()
        {
            IsDirty = false;
            Moved = false;
            CanCollidesWithList = new List<CollisionCategory>();
            CollisionList = new List<ICollidable>();
            CoefRestitution = 1.0f;            
            CollisionSpheres = new List<CollisionSphere>();
            CollisionDetectionPoints = new List<Location>();
            IsVisible = true;
            CollisionManagement = CollisionManagement.Manual;
            CollisionShape = CollisionShapes.Sphere;
            Scale = Vector2.One;
            _quandrants = new List<Quadrant>();
            Mass = 1.0f;
            RenderStrategy = RenderStrategy.Standard;
        }

        protected WiMo.Games.Size _size;

        public virtual WiMo.Games.Size Size
        {
            get { return new Size((_size.Width * Scale.X), (_size.Height * Scale.Y)); ; }
            set { _size = value; }
        }

        public virtual WiMo.Games.Size UnscaledSize
        {
            get { return _size; }
            set {
                _boundingRect = Rectangle.Empty;
                _size = value; 
            }
        }

        private PointF _position;
        public PointF Position
        {
            get{return new PointF(_position.X * Scale.X, _position.Y * Scale.Y); ;}
            set
            {
                if (_position != value)
                {
                    //if (this.Quandrants.Count > 0)
                    //    WiMo.Games.Quadrant.Root.Remove(this);

                    Invalidate();

                    _position = value;
                }
            }
        }

        public PointF UnscaledPosition
        {
            get { return _position; }
            set { _position = new PointF(value.X / Scale.X, value.Y / Scale.Y); }
        }

        protected Rectangle _boundingRectOverride = Rectangle.Empty;
        private Rectangle _boundingRect = Rectangle.Empty;
        public virtual Rectangle BoundingRect
        {
            get 
            {
                if (_boundingRect == Rectangle.Empty)
                {

                    if (_boundingRectOverride == Rectangle.Empty)
                    {
                        if (Origin == Origin.Center)
                            _boundingRect = new Rectangle(Position.Point.X - (Size.Width >> 1), Position.Point.Y - (Size.Height >> 1), Size.Width, Size.Height);
                        else
                            _boundingRect = new Rectangle(Position.Point.X, Position.Point.Y, Size.Width, Size.Height);
                    }
                    else
                    {
                        if (Origin == Origin.Center)
                            _boundingRect = new Rectangle(Position.Point.X - (_boundingRectOverride.Size.Width >> 1), Position.Point.Y - (_boundingRectOverride.Size.Height >> 1), _boundingRectOverride.Width, _boundingRectOverride.Height);
                        else
                            _boundingRect = new Rectangle(Position.Point.X + _boundingRectOverride.X, Position.Point.Y + _boundingRectOverride.Y, _boundingRectOverride.Width, _boundingRectOverride.Height);
                    }
                }

                return _boundingRect;
            }
        }

        public virtual void ApplyTorque(float torque)               
        {
            Torque += torque;
        }        

        private Vector2 _velocity = Vector2.Zero;
        public Vector2 Velocity
        {
            get{return _velocity;}
            set{_velocity = value;}
        }

        public WiMo.Games.Physics.ISpring Spring { get; set; }

        public virtual void BeginUpdate() { }
        public virtual void ApplyForce(Vector2 force)
        {
            Force += force;
        }

        public virtual void Move(Vector2 moveBy)
        {
            Position = new PointF(UnscaledPosition.X + moveBy.X, UnscaledPosition.Y + moveBy.Y);
        }

        public virtual void Update(GameTime gameTime){ }
        public virtual object DrawInformation { get; set; }
        public abstract void Render(IDisplay display);
        public virtual void Dispose()
        {
            var disposable = DrawInformation as IDisposable;
            if (disposable != null)
                disposable.Dispose();
        }

        public event EventHandler<CollisionEventArgs> Collision;
        public event EventHandler<CollisionEventArgs> Seperation;

        public float CoefRestitution { get; set; }

        public virtual bool CheckForCollision(ISprite sprite)
        {
            if (!IsVisible || !sprite.IsVisible || 
                !CanCollidesWithList.Contains(sprite.CollisionCategory) ||                 
                sprite == this)
                return false;
            
            var collided = false;

            if (this.CollisionList.Contains(sprite))
            {
                collided = !sprite.CollisionManager.DetectSeperation(this, sprite);
                if(!collided)
                {
                    this.OnSeperation(new CollisionEventArgs(sprite));
                    this.CollisionList.Remove(sprite);
                    sprite.OnSeperation(new CollisionEventArgs(this));
                    sprite.CollisionList.Remove(this);                    
                }
            }
            else
            {
                collided = sprite.CollisionManager.DetectCollision(this, sprite);
                
                if (collided)
                {
                    OnCollide(new CollisionEventArgs(sprite));
                    CollisionList.Add(sprite);
                    sprite.OnCollide(new CollisionEventArgs(this));
                    sprite.CollisionList.Add(this);
                    if (CollisionManagement == CollisionManagement.Auto)
                        sprite.PhysicsManager.UpdateForcesAfterCollision(this, sprite);
                }
            }

            return collided;
        }

        public CollisionCategory CollisionCategory { get; set; }

        public void CollideWithBorder(CollisionEventArgs.Border border)
        {
            OnCollide(new CollisionEventArgs(border));
        }

        public List<CollisionCategory> CanCollidesWithList { get; private set; }
        public List<ICollidable> CollisionList { get; private set; }

        public void OnCollide(CollisionEventArgs args)
        {
            if(Collision != null)
                Collision(this, args);
        }

        public void OnSeperation(CollisionEventArgs args)
        {
            if (Seperation != null)
                Seperation(this, args);
        }

        public CollisionManagement CollisionManagement { get; set; }

        public virtual IGeometry Geometry {get; private set;}

        public Vector2 GetDistance(ISprite sprite)
        {
            Vector2 delta;
            Vector2 nDelta = new Vector2();

            switch (this.CollisionShape)
            {
                case CollisionShapes.LineList:
                    switch (sprite.CollisionShape)
                    {
                        case CollisionShapes.LineList:
                            throw new NotImplementedException();
                        case CollisionShapes.Rectangle:
                            break;
                        case CollisionShapes.Sphere:
                            break;
                        case CollisionShapes.SphereList:
                            throw new NotImplementedException();
                    }
                    break;
                case CollisionShapes.Rectangle:
                    switch (sprite.CollisionShape)
                    {
                        case CollisionShapes.LineList:
                            for (var idx = 0; idx < sprite.CollisionList.Count - 1; ++idx)
                            {

                            }

                            break;
                        case CollisionShapes.Rectangle:
                        case CollisionShapes.Sphere:
                            delta = MathHelper.Distance(this.Position, sprite.Position);
                            nDelta = delta.Clone();
                            nDelta.Abs();
                            break;
                        case CollisionShapes.SphereList:
                            throw new NotImplementedException();
                    }
                    break;
                case CollisionShapes.Sphere:
                    switch (sprite.CollisionShape)
                    {
                        case CollisionShapes.LineList:
                            break;
                        case CollisionShapes.Rectangle:
                        case CollisionShapes.Sphere:
                            delta = MathHelper.Distance(this.Position, sprite.Position);
                            nDelta = delta.Clone();
                            nDelta.Abs();

                            break;
                        case CollisionShapes.SphereList:
                            throw new NotImplementedException();
                    }
                    break;
                case CollisionShapes.SphereList:
                    throw new NotImplementedException();
                //switch (sprite2.CollisionShape)
                //{
                //    case CollisionShapes.LineList:
                //        break;
                //    case CollisionShapes.Rectangle:
                //        break;
                //    case CollisionShapes.Sphere:
                //        break;
                //    case CollisionShapes.SphereList:
                //        break;
                //}
            }


            return nDelta;
        }
    }
}
